/**
 * 
 */
package com.vision.core.sv.security;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Transactional;

import com.vision.core.cm.db.data.User;
import com.vision.core.cm.db.data.User.UserStatus;
import com.vision.core.sv.db.mgr.UserManager;

/**
 * Class that performs the authentication process when a {@link User} logs in.
 * See {app name}-context-security.xml
 * 
 * @author Mark
 *
 */
public class AuthenticationManager implements AuthenticationProvider {
	
	private static final Logger log = LoggerFactory.getLogger(AuthenticationManager.class);

	private UserManager manager;
	
	@Autowired
	public void setUserManager(UserManager manager) {
		this.manager = manager;
	}
	
	@Transactional
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		if (authentication.getName() == null || authentication.getCredentials() == null)
			throw new BadCredentialsException("Invalid username and/or password.");
		
		log.info("Authenticating user: {}", authentication.getName());
		
		User user = manager.getById(authentication.getName());
		String encodedCredentials = Encrypt.encode(authentication.getCredentials().toString());
		
		// Check if passwords match
		if (user == null || !user.getPassword().equals(encodedCredentials))
			throw new BadCredentialsException("Invalid username and/or password.");
		
		// Check if active
		if (UserStatus.ACTIVE != user.getStatus())
			throw new UsernameNotFoundException("The user account is not active.");

		// Spring-Security roles
		Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
		
		// Update the last_login
		user.setLastLogin(new Timestamp(System.currentTimeMillis()));
		manager.update(user);
		
		// Create the user's session
		Session session = new Session(user, authorities);
		
		// TODO Strip out user password
		RememberMeAuthenticationToken token = new RememberMeAuthenticationToken("skey", session, session.getAuthorities());
		token.setAuthenticated(true);
		
		log.info("User '{}' was authenticated.", user.getUsername());
		
		return token;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return Authentication.class.isAssignableFrom(clazz);
	}

}